package pers.lxl.structure.linkedlist;

/**
 * 自定义链表
 *
 * @date & @author: 2018/12/16 20:32 & lxl
 */
public class LinkedList<E> {

    /**
     * 链表中存放数据的节点
     */
    private class Node {
        /**
         * 存放数据
         */
        E e;

        /**
         * 指向下一个节点
         */
        Node next;

        public Node() {
            this(null, null);
        }

        public Node(E e) {
            this(e, null);
        }

        public Node(E e, Node next) {
            this.e = e;
            this.next = next;
        }

        @Override
        public String toString() {
            return e.toString();
        }
    }

    /**
     * 定义一个虚拟头节点, 方便后续操作
     */
    private Node dummyHead;

    /**
     * 链表中元素的个数
     */
    private int size;

    public LinkedList() {
        dummyHead = new Node(null);
        size = 0;
    }

    /**
     * 获取链表中元素的个数
     *
     * @return 元素的个数
     */
    public int getSize() {
        return size;
    }

    /**
     * 查看链表是否为空
     *
     * @return true = 为空, false = 不为空
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 在链表的指定索引处添加元素
     *
     * @param index 要添加元素的索引
     * @param e     元素
     */
    public void add(int index, E e) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("Add failed. Require index >= 0 and index <= size.");
        }

        // 将头节点给prev,使用for循环让prev在链表中往后移动到要插入的位置的前一个位置(遍历链表)
        Node prev = dummyHead;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        prev.next = new Node(e, prev.next);
        size++;
    }

    /**
     * 添加一个元素到链表的第一个位置
     *
     * @param e 元素
     */
    public void addFirst(E e) {
        add(0, e);
    }

    /**
     * 添加一个元素到链表的最后一个位置
     *
     * @param e 元素
     */
    public void addLast(E e) {
        add(size, e);
    }

    /**
     * 从链表中获取指定索引位置的元素
     *
     * @param index 索引
     * @return 元素
     */
    public E get(int index) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("Get failed. Require index >= 0 and index <= size.");
        }

        Node currentNode = dummyHead.next;

        for (int i = 0; i < index; i++) {
            currentNode = currentNode.next;
        }

        return currentNode.e;
    }

    /**
     * 获得链表中和第一个元素
     *
     * @return 元素
     */
    public E getFirst() {
        return get(0);
    }

    /**
     * 获得链表中和最后一个元素
     *
     * @return 元素
     */
    public E getLast() {
        return get(size - 1);
    }


    /**
     * 修改链表中指定索引的元素
     *
     * @param index 索引
     * @param e     元素
     */
    public void set(int index, E e) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("Add failed. Require index >= 0 and index <= size.");
        }

        Node currentNode = dummyHead.next;
        for (int i = 0; i < index; i++) {
            currentNode = currentNode.next;
        }
        currentNode.e = e;
    }

    /**
     * 查看链表中是否有这个元素
     *
     * @param e 元素
     * @return true = 有, false = 没有
     */
    public boolean contains(E e) {
        Node currentNode = dummyHead.next;
        while (currentNode != null) {
            if (currentNode.e.equals(e)) {
                return true;
            }
            currentNode = currentNode.next;
        }
        return false;
    }

    public E remove(int index) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("Add failed. Require index >= 0 and index <= size.");
        }

        Node pre = dummyHead;

        for (int i = 0; i < index; i++) {
            pre = pre.next;
        }

        Node retNode = pre.next;
        pre.next = retNode.next;
        retNode.next = null;
        size--;

        return retNode.e;
    }

    public E removeFirst() {
        return remove(0);
    }

    public E removeLast() {
        return remove(size - 1);
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();

        for (Node cur = dummyHead.next; cur != null; cur = cur.next) {
            builder.append(cur + "-->");
        }

        builder.append("NULL");
        return builder.toString();
    }
}
